package dgut.party.business.security;

import dgut.party.business.security.exception.CustomAccessDeniedHandler;
import dgut.party.business.security.filter.JWTAuthenticationFilter;
import dgut.party.business.security.filter.JWTLoginFilter;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;

@Configuration
//@Order(SecurityProperties.DEFAULT_FILTER_ORDER)
@EnableWebSecurity
@Log4j2
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  BCryptPasswordEncoder bCryptPasswordEncoder;

  @Autowired
  JWTAuthenticationFilter jwtAuthFilter;

  @Autowired
  UserDetailsService userDetailsService;

  @Bean(name = "accessDecisionManager")
  public AccessDecisionManager accessDecisionManager() {
    List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<AccessDecisionVoter<? extends Object>>();
    decisionVoters.add(new UrlRoleBaseVoter());
    UnanimousBased accessDecisionManager = new UnanimousBased(decisionVoters);
    return accessDecisionManager;
  }

  @Bean
  public AccessDeniedHandler accessDeniedHandler() {
    return new CustomAccessDeniedHandler();
  }

  @Bean
  public UrlFilterInvocationSecurityMetadataSource mySecurityMetadataSource(
    FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource) {
    UrlFilterInvocationSecurityMetadataSource securityMetadataSource = new UrlFilterInvocationSecurityMetadataSource(
      filterInvocationSecurityMetadataSource);
    return securityMetadataSource;
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .cors().configurationSource(new CorsConfigurationSource() {
      @Override
      public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
        CorsConfiguration corsStrategy = new CorsConfiguration();
        corsStrategy.addAllowedOrigin("localhost");
        corsStrategy.addAllowedOrigin("beatmercy.tech");
        return corsStrategy;
      }
    })
      .and().exceptionHandling().accessDeniedHandler(accessDeniedHandler())
      .and().csrf().disable()
      .authorizeRequests()
      // 定义metadata source
      .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {

        @Override
        public <O extends FilterSecurityInterceptor> O postProcess(O fsi) {
          fsi.setSecurityMetadataSource(
            mySecurityMetadataSource(fsi.getSecurityMetadataSource()));
          return fsi;
        }
      })
      .accessDecisionManager(accessDecisionManager())
      .antMatchers("/api", "/api/**").permitAll()
      .and().addFilterAt(new JWTLoginFilter(super.authenticationManager()),
      UsernamePasswordAuthenticationFilter.class)
      .addFilterBefore(jwtAuthFilter, BasicAuthenticationFilter.class);

  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
  }
}
